Passed
Push — master ( 16b6a9...4331af )
by thomas
01:21
created

index.js ➔ polymodStep   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 24

Duplication

Lines 24
Ratio 100 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 6
c 1
b 1
f 1
nc 32
nop 1
dl 24
loc 24
ccs 14
cts 14
cp 1
crap 6
rs 8.5125
1
'use strict';
2
3 1
var BigInteger = require('bigi');
4 1
var ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
5
6
// pre-compute lookup table
7 1
var SEPARATOR = ':';
8 1
var CSLEN = 8;
9 1
var ALPHABET_MAP = {};
10 1
for (var z = 0; z < ALPHABET.length; z++) {
11 32
    var x = ALPHABET.charAt(z);
12 32
    if (ALPHABET_MAP[x] !== undefined) {
13
        throw new TypeError(x + ' is ambiguous');
14
    }
15 32
    ALPHABET_MAP[x] = z;
16
}
17
18
function polymodStep(pre) {
19 1382
    var b = pre.shiftRight(35);
20 1382
    var mask = BigInteger.fromHex('07ffffffff');
21
22 1382
    var v = pre.and(mask).shiftLeft(new BigInteger('5'));
23
24 1382
    if (b.and(new BigInteger('1')).intValue() > 0) {
25 611
        v = v.xor(BigInteger.fromHex('98f2bc8e61'));
26
    }
27 1382
    if (b.and(new BigInteger('2')).intValue()) {
28 591
        v = v.xor(BigInteger.fromHex('79b76d99e2'));
29
    }
30 1382
    if (b.and(new BigInteger('4')).intValue()) {
31 576
        v = v.xor(BigInteger.fromHex('f33e5fb3c4'));
32
    }
33 1382
    if (b.and(new BigInteger('8')).intValue()) {
34 643
        v = v.xor(BigInteger.fromHex('ae2eabe2a8'));
35
    }
36 1382
    if (b.and(new BigInteger('16')).intValue()) {
37 652
        v = v.xor(BigInteger.fromHex('1e4f43e470'));
38
    }
39
40 1382
    return v;
41
}
42
43
function prefixChk(prefix) {
44 27
    var chk = new BigInteger('1');
45 27
    for (var i = 0; i < prefix.length; ++i) {
46 270
        var c = prefix.charCodeAt(i);
47
48 270
        var mixwith = new BigInteger('' + (c & 0x1f));
49 270
        chk = polymodStep(chk).xor(mixwith);
50
    }
51
52 27
    chk = polymodStep(chk);
53 27
    return chk;
54
}
55
56
function encode(prefix, words) {
57
    // too long?
58 10
    if (prefix.length + CSLEN + 1 + words.length > 90) {
59 1
        throw new TypeError('Exceeds Base32 maximum length');
60
    }
61
62 9
    prefix = prefix.toLowerCase();
63
64
    // determine chk mod
65 9
    var chk = prefixChk(prefix);
66 9
    var result = prefix + SEPARATOR;
67 9
    for (var i = 0; i < words.length; ++i) {
68 275
        var _x = words[i];
69 275
        if (_x >>> 5 !== 0) {
70 1
            throw new Error('Non 5-bit word');
71
        }
72
73 274
        chk = polymodStep(chk).xor(new BigInteger('' + _x));
74 274
        result += ALPHABET.charAt(_x);
75
    }
76
77 8
    for (var _i = 0; _i < CSLEN; ++_i) {
78 64
        chk = polymodStep(chk);
79
    }
80 8
    chk = chk.xor(new BigInteger('1'));
81 8
    for (var _i2 = 0; _i2 < CSLEN; ++_i2) {
82 64
        var pos = 5 * (CSLEN - 1 - _i2);
83 64
        var v2 = chk.shiftRight(new BigInteger('' + pos)).and(BigInteger.fromHex('1f'));
84 64
        result += ALPHABET.charAt(v2.toString(10));
85
    }
86
87 8
    return result;
88
}
89
90
function decode(str) {
91 24
    if (str.length < 8) {
92 1
        throw new TypeError(str + ' too short');
93
    }
94 23
    if (str.length > 90) {
95 1
        throw new TypeError(str + ' too long');
96
    }
97
98
    // don't allow mixed case
99 22
    var lowered = str.toLowerCase();
100 22
    var uppered = str.toUpperCase();
101 22
    if (str !== lowered && str !== uppered) {
102 1
        throw new Error('Mixed-case string ' + str);
103
    }
104
105 21
    str = lowered;
106
107 21
    var split = str.lastIndexOf(SEPARATOR);
108 21
    if (split === -1) {
109 1
        throw new Error('No separator character for ' + str);
110
    }
111
112 20
    if (split === 0) {
113 1
        throw new Error('Missing prefix for ' + str);
114
    }
115
116 19
    var prefix = str.slice(0, split);
117 19
    var wordChars = str.slice(split + 1);
118 19
    if (wordChars.length < 6) {
119 1
        throw new Error('Data too short');
120
    }
121
122 18
    var chk = prefixChk(prefix);
123 18
    var words = [];
124 18
    for (var i = 0; i < wordChars.length; ++i) {
125 748
        var c = wordChars.charAt(i);
126 748
        var v = ALPHABET_MAP[c];
127 748
        if (v === undefined) {
128 1
            throw new Error('Unknown character ' + c);
129
        }
130
131 747
        chk = polymodStep(chk).xor(new BigInteger('' + v));
132
        // not in the checksum?
133 747
        if (i + CSLEN >= wordChars.length) {
134 136
            continue;
135
        }
136 611
        words.push(v);
137
    }
138
139 17
    if (chk.toString(10) !== '1') {
140 9
        throw new Error('Invalid checksum for ' + str);
141
    }
142
143 8
    return { prefix: prefix, words: words };
144
}
145
146
function convert(data, inBits, outBits, pad) {
147 18
    var value = 0;
148 18
    var bits = 0;
149 18
    var maxV = (1 << outBits) - 1;
150
151 18
    var result = [];
152 18
    for (var i = 0; i < data.length; ++i) {
153 514
        value = value << inBits | data[i];
154 514
        bits += inBits;
155
156 514
        while (bits >= outBits) {
157 477
            bits -= outBits;
158 477
            result.push(value >>> bits & maxV);
159
        }
160
    }
161
162 18
    if (pad) {
163 8
        if (bits > 0) {
164 8
            result.push(value << outBits - bits & maxV);
165
        }
166
    } else {
167 10
        if (bits >= inBits) {
168 1
            throw new Error('Excess padding');
169
        }
170 9
        if (value << outBits - bits & maxV) {
171 1
            throw new Error('Non-zero padding');
172
        }
173
    }
174
175 16
    return result;
176
}
177
178
function toWords(bytes) {
179 8
    return convert(bytes, 8, 5, true);
180
}
181
182
function fromWords(words) {
183 10
    return convert(words, 5, 8, false);
184
}
185
186
module.exports = { decode: decode, encode: encode, toWords: toWords, fromWords: fromWords };